package temperaturePackage;

import be.kuleuven.cs.som.annotate.Value;

/**
 * An enumeration introduction different temperature units. In its current form,
 * the class only supports Celsius, Kelvin and Farenheit.
 * 
 * @version 1.0
 * @author Jessa Bekker & Esther Alberts
 */
@Value
public enum TemperatureUnit {

	CELSIUS, KELVIN, FAHRENHEIT;

	/**
	 * Return the constants needed to compute the value of a temperature this
	 * unit to the other unit. this method returns an array with two values a
	 * (multiplier) and b (constant) so that value in this unit multiplied with
	 * a and added up with b gives the value in the other unit.
	 * 
	 * @param other
	 *            The unit to convert to.
	 * @return The resulting unit is effective. | result != null
	 * @return If this unit is the same as the other unit, one is returned for
	 *         the multiplier a. | if (this == other) | then (result[0] == 1)
	 * @return If this unit is the same as the other unit, zero is returned for
	 *         the constant b. | if (this == other) | then (result[1] == 0)
	 * @return The resulting multiplier is the inverse of the multiplier from
	 *         the other unit to this unit. | result[0].equals |
	 *         1/other.getExchangeConstants(this)[0]
	 * @return The resulting constant is the negative of the constant from the
	 *         other unit to this unit divided by the multiplier from the other
	 *         unit to this unit. | result[1].equals | -
	 *         other.getExchangeConstants
	 *         (this)[1]/other.getExchangeConstants(this)[0]
	 * @throws IllegalArgumentException
	 *             The given unit is not effective. | (other == null)
	 */
	public double[] getExchangeConstants(TemperatureUnit other)
			throws IllegalArgumentException {
		if (other == null)
			throw new IllegalArgumentException("Non effective unit!");
		if (exchangeNumbers[this.ordinal()][other.ordinal()] == null) {
			exchangeNumbers[this.ordinal()][other.ordinal()][0] = 1 / exchangeNumbers[other
					.ordinal()][this.ordinal()][0];
			exchangeNumbers[this.ordinal()][other.ordinal()][1] = -1
					* exchangeNumbers[other.ordinal()][this.ordinal()][1]
					/ exchangeNumbers[other.ordinal()][this.ordinal()][0];
		}
		return exchangeNumbers[this.ordinal()][other.ordinal()];
	}

	/**
	 * Variable referencing a three-dimensional array registering exchange rates
	 * and constants between temperature units. The first level is indexed by
	 * the ordinal number of the currency to convert from; the ordinal number to
	 * convert to is used to index the second level. The the third level is
	 * indexed 0 for the rates and 1 for the constants.
	 */
	private static double[][][] exchangeNumbers = new double[3][3][2];

	static {
		// Initialization of the upper part of the exchange table.
		// other rates are computed and registered the first time
		// they are queried.
		// Rates:
		exchangeNumbers[CELSIUS.ordinal()][CELSIUS.ordinal()][0] = 1;
		exchangeNumbers[KELVIN.ordinal()][KELVIN.ordinal()][0] = 1;
		exchangeNumbers[FAHRENHEIT.ordinal()][FAHRENHEIT.ordinal()][0] = 1;
		exchangeNumbers[CELSIUS.ordinal()][KELVIN.ordinal()][0] = 1;
		exchangeNumbers[CELSIUS.ordinal()][FAHRENHEIT.ordinal()][0] = 9 / 5;
		exchangeNumbers[KELVIN.ordinal()][FAHRENHEIT.ordinal()][0] = 9 / 5;
		// constants:
		exchangeNumbers[CELSIUS.ordinal()][CELSIUS.ordinal()][1] = 0;
		exchangeNumbers[KELVIN.ordinal()][KELVIN.ordinal()][1] = 0;
		exchangeNumbers[FAHRENHEIT.ordinal()][FAHRENHEIT.ordinal()][1] = 0;
		exchangeNumbers[CELSIUS.ordinal()][KELVIN.ordinal()][1] = 273.15;
		exchangeNumbers[CELSIUS.ordinal()][FAHRENHEIT.ordinal()][1] = 32;
		exchangeNumbers[KELVIN.ordinal()][FAHRENHEIT.ordinal()][1] = 32 - (9 / 5) * 273.15;
	}
}